package br.ufrj.management.pages.virtual;

import java.util.ArrayList;

public class MemoryManager {
	
	 static ArrayList<SimThread> running_threads;
	 static ArrayList<String> main_memory;
	 static int working_set_limit;
	 static int pages_per_process;
	 static int memory_size_in_frames;
	 static int n_request_calls;
	 static int n_request_calls_limit;
	
	
	public static void initialize(int memory_size, int n_max_threads, int request_calls_limit){
		n_request_calls = 0;
		n_request_calls_limit = request_calls_limit;
		memory_size_in_frames = memory_size;
		running_threads = new ArrayList<SimThread>(n_max_threads);
		main_memory = new ArrayList<String>(memory_size_in_frames);
		
		
		for (int i = 0; i < memory_size_in_frames; i++){
			main_memory.add(new String("-1"));
		}
		
		LogWriter.open_log_file();
		LogWriter.write_line_to_log("Inicializando Memoria Principal...");
		LogWriter.memory_dump(main_memory);
		LogWriter.close_log_file();
		System.out.println("Dump inicial da memoria escrito no Log");
	}
	
	synchronized public static void add_thread(SimThread thread){
		if (main_memory.size()-thread.getId()*4>=4)
		{
			running_threads.add(thread);
			main_memory.set(thread.getId()*4, thread.working_set_table.toString());
			main_memory.set(thread.getId()*4 + 1, "T" + thread.getId() + "P0");
			main_memory.set(thread.getId()*4 + 2, "T" + thread.getId() + "P1");
			main_memory.set(thread.getId()*4 + 3, "T" + thread.getId() + "P2");
			System.out.println("Id da Thread Criada: " + thread.getId() );
			LogWriter.write_safe_line_to_log("Id da Thread Criada: " + thread.getId() );
		}
		else
		{
			running_threads.add(null);
			System.out.println("Nao ha mais espaco disponivel!");
		}
	}
	
	synchronized public static void requestPage(int page, int id)
	{
		String output_string = "";
		n_request_calls++;
		
		for(int i=running_threads.get(id).working_set_table.size()-1; i>=0; i--)
		{
			if (running_threads.get(id).working_set_table.get(i).intValue()==page)
			{
				output_string = "Thread ID: " + id + " Message: Pagina " + page + " se encontra no Working set.";
				
				System.out.println(output_string);
				//System.out.println("Working Set: " + running_threads.get(id).working_set_table.toString());
				
				LogWriter.write_safe_line_to_log(output_string);
				//LogWriter.write_safe_line_to_log("Working Set: " + running_threads.get(id).working_set_table.toString());
				updatePageUsage(page,true,i,id);
				break;
			}
			if (i==0)
			{
				System.out.println("Thread ID: " + id + " Message: Page Fault!");
				LogWriter.write_safe_line_to_log("Thread ID: " + id + " Message: Page Fault!");
				output_string = "Thread ID: " + id + " Message: Pagina " + page + " nao se encontra no Working set.";
				
				System.out.println(output_string);
				LogWriter.write_safe_line_to_log(output_string);
				
				updatePageUsage(page,false,i,id);
				break;
			}
		}
		
		if(n_request_calls % n_request_calls_limit == 0 ){
			LogWriter.safe_memory_dump(main_memory);
		}
		
	}
	
	synchronized public static void updatePageUsage(Integer newPage, boolean isOnTable, int index, int id)
	{
		if (!isOnTable)
		{
			if (running_threads.get(id).working_set_table.get(0)!=-1)
			{
				
				System.out.println("Thread ID: "+ id + " Message: Executando LRU...");
				System.out.println("Sai pagina " + running_threads.get(id).working_set_table.get(0) + ". Entra pagina " + newPage);
				LogWriter.write_safe_line_to_log("Thread ID: "+ id + " Message: Executando LRU...");
				LogWriter.write_safe_line_to_log("Sai pagina " + running_threads.get(id).working_set_table.get(0) + ". Entra pagina " + newPage);
			}
			for(int i=0;i<running_threads.get(id).working_set_table.size()-1;i++)
			{
				running_threads.get(id).working_set_table.set(i, running_threads.get(id).working_set_table.get(i+1));
			
			}
		}
		else
		{
			for(int i=index;i<running_threads.get(id).working_set_table.size()-1;i++)
			{
				running_threads.get(id).working_set_table.set(i, running_threads.get(id).working_set_table.get(i+1));
			}			
		}
		running_threads.get(id).working_set_table.set(running_threads.get(id).working_set_table.size()-1, newPage);
		updateMainMemory(id);
		
		System.out.println("Working Set: " + running_threads.get(id).working_set_table.toString());
		LogWriter.write_safe_line_to_log("Working Set: " + running_threads.get(id).working_set_table.toString());
	}
	
	synchronized public static void updateMainMemory(int id)
	{
		main_memory.set(id*4, running_threads.get(id).working_set_table.toString());
		
		for(int i = 0; i < 3; i++){
			if(running_threads.get(id).working_set_table.get(i).intValue() == -1){
				main_memory.set(id*4 + i + 1, "T"+id+"X");
			}
			else{
				main_memory.set(id*4 + i + 1, "T"+id+"P"+running_threads.get(id).working_set_table.get(i));
			}
		}
		
	}

}
